home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Shareware Grab Bag
/
Shareware Grab Bag.iso
/
007
/
vtkerma1.arc
/
MSKERM.ASM
< prev
next >
Wrap
Assembly Source File
|
1986-02-13
|
56KB
|
2,295 lines
PAGE 59, 132
TITLE MSKERM -- Main module for MS-Kermit and descendents
; Update 20 Jan 86
IF1
%OUT >> Starting pass 1
ELSE
%OUT >> Starting pass 2
ENDIF
PUBLIC Prompt, DosNum, CurDsk, SwChar, Kermit, KrmRet, OldStk
PUBLIC SPath, PC_Type, PC_Subtype, Dos_Minor, TakRd, NoInt, OkInt
PUBLIC Get_memory_block, Quick_Push, Exit2, In_menu_mode
PUBLIC TakAdr, TakLev, Force_mono
Code SEGMENT PARA PUBLIC
Code ENDS
DataS SEGMENT PARA PUBLIC 'datas'
DataS ENDS
Stack SEGMENT PARA STACK 'STACK'
Stack ENDS
INCLUDE MsDefs.H
; Based on ...
;******************** Version 2.26 **********************************
; KERMIT, Celtic for "free"
;
; The name "Kermit" is a registered trade mark of Henson Associates, Inc.,
; used by permission
;
; Kermit-MS Program Version 2.26, July 26, 1984
;
; Based on the Columbia University KERMIT Protocol
;
; Copyright (C) 1982,1983,1984 Trustees of Columbia University
;
; Daphne Tzoar, Jeff Damens
; Columbia University Center for Computing Activities
; 612 West 115th Street
; New York, NY 10025
;
; Special thanks to Frank da Cruz, Bill Catchings, Steve Jensen, Herm Fischer,
; Vace Kundakci, and Bernie Eiben for their help and contributions
makseg EQU 26H
deffcb EQU 5cH
setblk EQU 4AH
exec EQU 4BH
env EQU 2CH ; environment address in psp
cline EQU 80H ; offset in psp of command line
namsiz EQU 64 ; Bytes for file name and size
maxnam EQU 10
chmod EQU 43H ; chmod call (used to test for file existence)
DataS SEGMENT PUBLIC 'datas'
EXTRN DTA:byte, comand:byte, flags:byte, pack:byte, trans:byte
EXTRN fcb:byte, cpfcb:byte, prmptr:word, inichk:byte
Help_text db Cr,Lf
Program_name
DB ' is an enhanced version of Columbia '
db "University's MS-Kermit program.",Cr,Lf,'This version supports '
db 'IBM PCs and compatibles as well as the DEC Rainbow.',Cr,Lf,Cr,Lf
Program_name
db ' has two main functions: Terminal Emulation and File '
db 'Transfer. In',Cr,Lf,'Terminal Emulation Mode, '
Program_name
DB ' permits '
db 'your microcomputer to act as a DEC',Cr,Lf,'VT100 terminal. '
db ' In File Transfer Mode, '
Program_name
DB ' permits you to exchange files'
db Cr,Lf,'with a remote computer running a program which '
db 'supports the Kermit file',Cr,Lf,'transfer protocol. '
db 'A third mode, Command Mode, is used to set parameters and '
db Cr,Lf,'control the program. Press ? while '
db 'typing a command for help, or press Esc',Cr,Lf
db 'for command completion.',Cr,Lf,Cr,Lf
db 'To use Terminal Emulation, you must first specify (or default) '
db 'all required',Cr,Lf,'parameters (PORT, BAUD rate, and PARITY),'
db " then press the IBM PC's F5 key ",'("Do"',Cr,Lf,'on Rainbow). '
db 'To get back to Command Mode, press F5 or Do again. While in'
db Cr,Lf,'Terminal Mode you can press F6 (PC only) '
db 'to get more help.',Cr,Lf,Cr,Lf
db 'To use File Transfer:',Cr,Lf
db ' 1) Establish a connection to a remote host computer;',Cr,Lf
db ' 2) Run Kermit (or a Kermit-based program) on the host in '
db 'SERVER mode;',Cr,Lf
db ' 3) Enter local Command Mode and type "SEND filename" to '
db 'send a file,',Cr,Lf
db ' or "GET filename" to receive a file from the host.'
db Cr,Lf,Cr,Lf,'$'
versio DB cr, lf ; Leading CrLf
verdef
DB ' running $'
On DB 'on $'
IBMsg DB 'IBM PC$'
RbMsg DB 'DEC Rainbow$'
GrMsg DB 'GRiD GRiDCase$'
Att6300Msg DB 'AT&T 6300$'
QuadRamsg DB 'QuadRam DataVue$'
MSDOS DB 'under MS-DOS$'
UkMsg DB 'MS-DOS OEM #$'
XT_str DB '/XT$'
Portable_str DB ' Portable$'
jr_str DB 'jr$'
AT_str DB ' AT$'
Compaq_str DB ' clone (Compaq)$'
Clone_str DB ' clone (?)$'
; DB ' under DOS v'
;dosvers DB '9.99'
EndVersio DB ' -- type ? or HELP for help',cr,lf,cr,lf,'$'
tmp db ?,'$'
crlf db cr,lf,'$'
ermes1 db '? Unrecognized command',cr,lf,'$'
ermes3 db '? Not confirmed',cr,lf,'$'
erms30 db '? Passed maximum nesting level for TAKE command',cr,lf,'$'
erms31 db '? File not found',cr,lf,'$'
erms32 db '? File(s) not found',cr,lf,'$'
erms33 db '? CHKDSK program not found on current disk',cr,lf,'$'
erms34 db '? This program runs only under DOS 2.0 and above',cr,lf,'$'
erms35 db '? Must specify program name',cr,lf,'$'
erms36 db '? Could not free memory',cr,lf,'$'
erms37 db '? Unable to execute program',cr,lf,'$'
erms38 db '? Not enough TAKE stack space left to parse filename',cr,lf,'$'
infms1 db 'Really erase *.*? $'
infms8 db 'File(s) erased',cr,lf,'$'
Echo_help DB ' Text to be echoed on screen$'
tmsg5 db '[Closing log file]',cr,lf,'$' ; [jd]
filhlp1 db ' Command file specification $'
filhlp2 db ' File specification (possibly wild) $'
filhlp3 db ' File spec (possibly wild) or confirm with carriage return$'
filmsg db ' File specification with optional path name $'
filwmsg db ' File specification (possibly wild) with optional path name $'
chkfil db 0,'CHKDSK COM'
chkflen EQU $-chkfil
tophlp DB ' One of the following:'
DB cr,lf,' '
DB 'BYE',tab,tab
DB 'CLOSE',tab,tab
DB 'CLS',tab,tab
DB 'CONNECT'
DB cr,lf,' '
DB 'DEFINE',tab,tab
DB 'DELETE',tab,tab
DB 'DIRECTORY',tab
DB 'DO'
DB cr,lf,' '
DB 'DO-SCRIPT',tab
DB 'DROP-DTR',tab
DB 'ECHO',tab,tab
DB 'EXIT'
DB cr,lf,' '
DB 'FINISH',tab,tab
DB 'GET',tab,tab
DB 'HELP',tab,tab
DB 'LOCAL'
DB cr,lf,' '
DB 'LOG',tab,tab
DB 'LOGOUT',tab,tab
DB 'MENU',tab,tab
DB 'NPSEND'
DB cr,lf,' '
DB 'PUSH',tab,tab
DB 'QUIT',tab,tab
DB 'RECEIVE',tab,tab
DB 'REMOTE'
DB cr,lf,' '
DB 'RUN',tab,tab
DB 'SCRIPT-FILE',tab
DB 'SEND',tab,tab
DB 'SERVER'
DB cr,lf,' '
DB 'SET',tab,tab
DB 'SHOW',tab,tab
DB 'SPACE',tab,tab
DB 'STATUS'
DB cr,lf,' '
DB 'TAKE',tab,tab
DB 'XRECEIVE',tab
DB 'XSEND'
DB '$'
lochlp DB cr,lf,' DELETE file'
DB cr,lf,' DIRECTORY [filespec]'
DB cr,lf,' PUSH to command interpreter'
DB cr,lf,' RUN program'
DB cr,lf,' SPACE remaining on current disk'
DB '$'
; COMND tables
yestab DB 2
mkeyw 'NO',0
mkeyw 'YES',1
comtab DB 36
mkeyw 'BYE',bye
mkeyw 'C',telnet
mkeyw 'CLOSE',clscpt
mkeyw 'CLS',Clear_screen
mkeyw 'CONNECT',telnet
mkeyw 'DEFINE',dodef
mkeyw 'DELETE',delete
mkeyw 'DIRECTORY',direct
mkeyw 'DO',docom
mkeyw 'DO-SCRIPT', Do_Script
mkeyw 'DROP-DTR',DoDrop
mkeyw 'ECHO', Echo_command
mkeyw 'EXIT',exit
mkeyw 'FINISH',finish
mkeyw 'GET',get
mkeyw 'HELP',help
mkeyw 'LOCAL',lclcmd
mkeyw 'LOG',setcpt
mkeyw 'LOGOUT',logout
mkeyw 'MENU', Menu
mkeyw 'NPSEND', Non_protocol_send
mkeyw 'PUSH',dopush
mkeyw 'QUIT',exit
mkeyw 'RECEIVE',read
mkeyw 'REMOTE',remote
mkeyw 'RUN',run
mkeyw 'SCRIPT-FILE', Script_File
mkeyw 'SEND',send
mkeyw 'SERVER',server
mkeyw 'SET',setcom
mkeyw 'SHOW',showcmd
mkeyw 'SPACE',chkdsk
mkeyw 'STATUS',status
mkeyw 'TAKE',take
mkeyw 'XRECEIVE', XReceive
mkeyw 'XSEND', XSend
loctab DB 5
mkeyw 'DELETE',delete
mkeyw 'DIRECTORY',direct
mkeyw 'PUSH',dopush
mkeyw 'RUN',run
mkeyw 'SPACE',chkdsk
shotab DB 2
mkeyw 'KEY',shokey
mkeyw 'MACRO',shomac
; Program storage
Hold_di DW ? ; Place to hold di reg over call to COMND
oldstk DW ? ; Storage for system stack
oldsts DW ? ; System stack segment
ssave DW ? ; Original SS when doing CHKDSK
siz DW ? ; Memory size
in3ad DD 0 ; Original break interrupt addresses. [25]
curdsk DB 0 ; Current disk
origd DB 0 ; Original disk
fildat DB 0 ; Manipulate file data/time creation
DB 0
taklev DB 0 ; Take levels. [25t]
takadr DW takstr-(size takinfo) ; Pointer into structure. [25t]
temp DW 0
temp1 DW ? ; Temporary storage
temp2 DW ?
temp3 DW ?
temp4 DW ?
psp DW ?
divst DW 0
Max_count_of_blocks=10 ; Size of memory block table
Count_of_allocated_blocks DW 0 ; Number of entries in memory block table
Block_table DW Max_count_of_blocks DUP (0) ; Memory block table
PC_Type DB 0 ; Zero for IBM
; 2 for Rainbow
; 4 for GRiD GRiDCase
; 6 for AT&T 6300
; 8 for QuadRam DataVue
; 20 for unknown hardware (crash is likely)
PC_Subtype DB 0 ; Zero for vanilla PC,
; 1 for PC/XT
; 2 for PCjr
; 3 for PC AT
; 4 for Portable PC
; 90 for Compaq
; 99 for PC clones
takstr DB (size takinfo) * maxtak dup(?)
; Build a 12 char FCB style INI file name
ininam DB 0
IniXXX DB ' INI' ; Start with 8 spaces followed by INI
IniYYY LABEL BYTE ; A label following the base name
ORG IniXXX ; Go back to where the 8 space are
Program_name ; Overlay the start with our file name
ORG IniYYY ; Go back to proper location
; Build a DOS 2.0 style INI file name
IniNm2 LABEL BYTE
Program_name
DB '.INI',0 ; Name of INIT file
IniNm2_len EQU $ - IniNm2
nambuf DB maxnam * namsiz dup (?)
cmdnam DB namsiz dup (?)
exefcb DB fcbsiz dup (?)
exefcb2 DB fcbsiz dup (?)
exearg DW ? ; segment addr of environment (filled in below)
DD 0 ; ptr to cmd line (filled in below)
DD exefcb ; default fcb
DD exefcb2 ; second default fcb
dircmd DB ' /c dir '
dirclen EQU $-dircmd
dirnam DB 50h dup (?)
chkdcmd DB 'chkdsk.com'
chkdlen EQU $-chkdcmd
dosnum DB ? ; dos version number
Dos_Minor DB ? ; Dos minor version number
Force_mono DB ? ; Use monochrome attributes on color displays
pthnam DB 'PATH='
pthlen EQU $-pthnam
pthbuf DB 160 dup (?) ; buffer for path definition
defpth DB '\', 70 dup (?) ; buffer for default path
cmspnam DB 'COMSPEC='
cmsplen EQU $-cmspnam
cmspbuf DB '\command.com', 0 ; Default name
DB 30 dup (?) ; Some additional space
TFile DB 100 dup (?) ; Temp space for file names
swchar DB '\' ; Default switch character
In_menu_mode DB 0 ; If non-0, we are in menu (not command) mode
NoInt_Count DB 0 ; Number of times we have been NoInt-ed
LastChar DB 0 ; Last char we read in NPSEND
Number DB 8 DUP (?) ; Place to hold a number
DataS ENDS
Code SEGMENT PUBLIC
EXTRN cmblnk:near, locate:near, logout:near, Drop_DTR:NEAR
EXTRN bye:near, telnet:near, finish:near, comnd:near, Telnet2:NEAR
EXTRN read:near, remote:near, send:near, status:near, get:near
EXTRN dodisk:near, serrst:near, setcom:near, OutChr:NEAR
EXTRN clscpi:near, clscpt:near, getbaud:near, dodef:near, setcpt:near
EXTRN docom:near, Set_up_script_processor:NEAR, server:near, lclini:near
EXTRN shokey:near, shomac:near, XReceive:NEAR, XSend:NEAR
EXTRN Script_File:NEAR, Do_Script:NEAR, Menu:NEAR, Quick_menu:NEAR
ASSUME cs:Code, ds:Datas, ss:Stack
Critical DD ? ; Address of original Critical Error Handler
Error_handling_installed DB 0 ; Flag set if error handling has been installed
Last_Critical_Error DW 00FFh ; Most recent critical error, if any
Start PROC FAR
push ds ; Save system data area
sub ax,ax ; Get a zero
push ax ; Put zero return addr on stack
mov ax, SEG DataS ; Initialize DS
mov ds,ax
sub ax,ax
mov oldstk, sp ; Save old stack pointer
mov ax,es:[2] ; In program segment prefix
mov siz,ax ; Pick up memory size
mov psp,es
mov ax, ds ; Copy ds
mov es, ax ; to es
ASSUME es:datas
mov ah, 30h ; Code to get DOS version number
int Dos ; Do it
cmp al, 2 ; DOS 2.0 or higher?
jae DOS_is_Ok ; Not a problem
mov ah, PrStr ; Code to print a string
mov dx, OFFSET ErMs34 ; Message complaining about lousy DOS
int Dos ; Type it
ret ; Return to DOS
DOS_is_Ok:
mov ax, (33H * 256) + 1 ; Code to set BREAK state
sub dl, dl ; Force it off NOW!
int Dos ; We use ^C heavily, we have to control it
push es ; Save es register
mov es, psp ; Point to psp again
mov ax, es:[Env] ; Get environment ptr
mov exearg, ax ; Put into argument block
; Give back memory which we don't need from our initial allocation
; (DOS gives us all available memory)
mov bx, OFFSET Stk + 15 ; End of program
mov cl, 4
shr bx, cl ; Compute # of paragraphs in last segment
mov ax, SEG stack ; Last segment, either alphabetically or
; sequentially
sub ax, Psp ; Minus beginning..
add bx, ax ; # of paragraphs occupied
mov ah, SetBlk ; Try to change size of our memory block
int Dos ; Success?
jnc SHS_1 ; Nope..
pushf ; Save flags
mov ah, PrStr
mov dx, OFFSET ErMs36 ; Complain
int Dos
popf ; Restore flags
SHS_1: pop es ; Unclobber register
; Before beginning, establish our own quick-o shortcut in case a drive
; on the PATH is without disk ... in that case, just bomb the call, don't
; do the usual "Abort, Retry or Ignore"
call Set_up_error_handling ; Establish our routine as the one to use
mov ah, prstr ; Print start of version header
mov dx, OFFSET Versio
int Dos
; Get the DOS version number and the "vendor number" for the vendor of this
; copy of DOS ... anytime we are running PC-DOS (on whatever machine) we think
; we are on "an IBM". If the user thinks PC-DOS runs on his machine, lets
; assume that the machine is a perfect enough clone for our purposes ...
PUBLIC See_what_machine_we_are_on
See_what_machine_we_are_on:
mov ah, 30h ; Code to get DOS version & vendor numbers
int Dos ; Do it
mov dosnum, al ; Remember major Dos version
mov Dos_Minor, ah ; Store minor version number also
mov Force_mono, 0 ; Default to "use color on color display"
mov ah, PrStr ; Set up to type strings
PUBLIC Check_vendor
Check_vendor:
cmp bh, 0FFh ; Generic MS-DOS?
jne Not_MSDOS ; No
mov dx, OFFSET MSDOS ; "under MS-DOS"
int Dos ; Type it
jmp SHORT Finish_banner ; Go finish it up
Not_MSDOS:
mov dx, OFFSET On ; "on "
int Dos ; Type it
cmp bh, 0 ; Is the vendor IBM?
jne Not_IBM ; No
call Vendor_is_IBM ; Use special rtn to determine type of IBM
jmp SHORT Finish_banner ; Go finish it up
Not_IBM: cmp bh, 16h ; Is the vendor DEC?
jne Not_DEC ; No
mov PC_Type, 2 ; Type is DEC
mov dx, OFFSET RbMsg ; DEC Rainbow
int Dos
jmp SHORT Finish_banner
Not_DEC: cmp bh, 2Eh ; Is the vendor GRiD?
jne Not_GRiD ; No
mov PC_Type, 4 ; Type is GRiD
mov dx, OFFSET GrMsg ; GRiD GRiDCase
int Dos
jmp SHORT Finish_banner
Not_GRiD: cmp bh, 77h ; Is the vendor AT&T?
jne Not_Att ; No
mov PC_Type, 6 ; Type is AT&T 6300
mov dx, OFFSET Att6300Msg
int Dos
jmp SHORT Finish_banner
PUBLIC No_body
Not_Att: cmp bh, 60 ; Is the vendor QuadRam?
jne No_body ; No
mov PC_Type, 8 ; Type is QuadRam DataVue 25
mov dx, OFFSET QuadRamsg
int Dos
jmp SHORT Finish_banner
PUBLIC No_body
No_body:
push bx ; Save reg
mov PC_Type, 20 ; Type is unknown
mov dx, OFFSET UkMsg ; Unknown hardware
int Dos
pop bx ; Get back reg
cld ; Forwards
mov al, bh ; Copy OEM # to al
mov ah, 0 ; Zero high half
mov di, OFFSET Number ; Point at Number
call nout2 ; Do the number
mov al, '$' ; Get a dollar sign
stosb ; Put it down
mov ah, PrStr ; Code to type to screen
mov dx, OFFSET Number ; Point this at Number
int Dos ; Do it
Finish_banner:
mov dx,offset EndVersio ; Close it out
int dos
mov ah,setdma ; Set disk transfer address
mov dx,offset DTA
int dos
call getbaud ; Get the baud rate. [25]
call dodisk ; See how many disk drives we have. [21a]
call setint
mov ah,gcurdsk ; Get current disk
int dos
inc al ; We want 1 == A (not zero)
mov curdsk,al
mov origd,al ; Remember original disk we started on
mov es,psp
mov ax,es:[env] ; pick up environment address
push ax
call getpath ; get the path from the environment
pop ax ; get environment back
call getcsp ; get comspec from environment as well
mov Flags.ExtFlg, 0 ; Clear EXIT flag before LCLINI just in case
call LclIni ; Do local initialization
cmp Flags.ExtFlg, 0 ; Some LclIni routines flag fatal errors
je KrmGo ; No, finish startup
jmp KrmEnd ; If so jump to KRMEND
KrmGo: call gcmdlin ; read command line
call rdinit ; read kermit init file
call packlen ; Packet length in case do server comand
; This is the main KERMIT loop. It prompts for and gets the users commands
Kermit: mov ax,ds
mov es,ax ; make sure this addresses data segment
mov dx,prmptr ; get prompt
call prompt ; Prompt the user
mov pack.state,0 ; Clear the state
mov flags.cxzflg,0 ; Reset each itme
mov Flags.XFlg, 0 ; This one also [tm]
mov ah, Flags.Real_RemFlg ; Pick this up
mov Flags.RemFlg, ah ; Copy to here
mov ah,inichk ; Original or set checksum length
mov trans.chklen,ah ; Reset just in case
mov dx,offset comtab
mov bx,offset tophlp
mov comand.cmcr,1 ; Allow bare CR's
mov ah,cmkey
call comnd
jmp kermt2
mov comand.cmcr,0 ; Not anymore
call bx ; Call the routine returned
krmret: jmp kermt3
cmp flags.extflg,0 ; Check if the exit flag is set
jne krmend ; If so jump to KRMEND
jmp kermit ; Do it again
kermt2: mov dx,offset ermes1 ; Give an error
jmp short kermt4
kermt3: mov dx,offset ermes3 ; Give an error
kermt4: cmp flags.cxzflg,'C' ; some sort of abort?
je kermit ; yes, don't print error message
mov ah,prstr
int dos
mov flags.droflg,0 ; Reset drive override flag
mov flags.nmoflg,0 ; Reset filename override flag
mov flags.getflg,0 ; May as well do this one
mov flags.cmrflg,0 ; This one too
jmp kermit
krmend: call serrst ; Just in case the port wasn't reset. [21c]
mov dl,origd ; Original disk drive
dec dl ; Want A == 0
mov ah,seldsk ; Reset original disk just in case
int dos
call Return_all_memory ; Clean up after ourselves
call Restore_normal_error_handling ; Return to normal situation
mov sp, OldStk ; Fix up stack just in case
mov ax, 4C00h ; Code to exit with no error
int Dos ; Quit to DOS
ret ; Should not return here!
START ENDP
; Special routine to figure out what kind of IBM flavor PC we are running on
Vendor_is_IBM:
mov PC_Type, 0 ; Type is IBM
mov dx, OFFSET IBMsg ; IBM PC
int Dos
mov bx, 0FFFFh ; Highest segment
push es ; Save es
mov es, bx ; Set up es
mov al, es:[0Eh] ; "Machine-Sensitive Code", says IBM
pop es ; Restore es
cmp al, 0FFh ; Vanilla PC?
jne IBM_0 ; No, keep working
ret ; Yes, done here
IBM_0: cmp al, 0FEh ; PC/XT?
jne NotXT
; Its down to /XT versus Portable PC (same BIOS)
mov bx, 040h ; DOS's data segment
push es ; Save es
mov es, bx ; Set up es
mov al, es:[075h] ; Count of fixed disk drives
pop es ; Restore es
cmp al, 0 ; Are there any fixed disks on this machine?
jne IsXT ; Yes there are, must be an XT
mov PC_Subtype, 4 ; Type is Portable PC
mov dx, OFFSET Portable_str
mov Force_mono, 1 ; Portable has monochrome amber screen
jmp SHORT Suffix
IsXT: mov PC_Subtype, 1 ; Type is PC/XT
mov dx, OFFSET XT_str
jmp SHORT Suffix
NotXT: cmp al, 0FDh ; PCjr?
jne Notjr
mov PC_Subtype, 2 ; Type is PCjr
mov dx, OFFSET jr_str
jmp SHORT Suffix
Notjr: cmp al, 0FCh ; PC AT?
jne NotAT
mov PC_Subtype, 3 ; Type is PC AT
mov dx, OFFSET AT_str
jmp SHORT Suffix
NotAT: cmp al, 02Dh ; Compaq?
jne NotCompaq
mov PC_Subtype, 90 ; Type is Compaq
mov dx, OFFSET Compaq_str
mov Force_mono, 1 ; Compaq has green monochrome screen
jmp SHORT Suffix
NotCompaq:
mov PC_Subtype, 99 ; Type is PC clone
mov dx, OFFSET Clone_str
Suffix: int Dos ; Finish up DOS call
ret ; Done here
; CLS command -- used to clear the screen
Clear_screen PROC
mov ah,cmcfm
call comnd ; Get a confirm
jmp r
call CmBlnk ; Call routines to clear the screen
call Locate ; then home the cursor ...
jmp RSkp ; Get the prompt
Clear_screen ENDP
; DROP-DTR command -- used to hang up the line
DoDrop PROC
mov ah, cmcfm
call comnd ; Get a confirm
jmp r
call SerRst ; Reset serial port (uninstall interrupt rtn)
call Drop_DTR ; Call routine to drop DTR
jmp RSkp
DoDrop ENDP
; ECHO command -- display some text for the user to see
Echo_command PROC
mov ah, CMTXT ; Read in a line of text
mov bx, OFFSET TFile ; Temp area for text, max 100 chars
mov dx, OFFSET Echo_help ; Simple message for help
call comnd ; Do it
jmp RSkp
mov di, OFFSET TFile ; Point to start of message
mov bl, ah ; Copy length to bl
sub bh, bh ; Clear high half
add di, bx ; Move to end of message
mov al, Cr ; Return
stosb ; Store it
mov al, Lf ; Line feed
stosb ; Store it
mov al, '$' ; Dollar sign
stosb ; Store it
mov dx, OFFSET TFile ; Point to text again
mov ah, PrStr ; Code to type a string
int Dos ; Type it
jmp rskp
Echo_command ENDP
; This is the 'exit' command. It leaves KERMIT and returns to DOS
EXIT PROC NEAR
mov ah, cmcfm
call comnd ; Get a confirm
jmp r
test flags.capflg,0FFH ; capturing?
jz exit2 ; no, keep going
mov dx,offset tmsg5
mov ah,prstr
int Dos
call clscpi
nop ; this skip returns..
nop
nop
exit2: mov flags.extflg, 1 ; Set the exit flag
jmp rskp ; Then return to system
EXIT ENDP
; This is the 'help' command. It gives a very brief intro to the program
HELP PROC NEAR
mov ah,cmcfm
call comnd ; Get a confirm
jmp r
mov ah,prstr ; Print a string to the console
mov dx,offset Help_text ; The address of the help message
int dos
jmp rskp
HELP ENDP
lclcmd proc near
mov ah,cmkey
mov dx,offset loctab
mov bx,offset lochlp
call comnd
jmp r
call bx
nop
nop
nop
jmp rskp
lclcmd endp
PUBLIC SetInt
; Don't ignore ^C when in debug mode
SETINT PROC NEAR
push es ; Save reg
mov ax, 3523h ; Want addr for int 23h
int Dos ; Get address of this interrupt
mov WORD PTR in3ad, bx ; Store offset (IP)
mov ax, es ; Copy es to ax
mov WORD PTR in3ad+2, ax ; Store the segment (CS)
pop es ; Get back normal es
push ds ; Save ds
mov ax, cs ; Copy cs ...
mov ds, ax ; ... to ds
mov ax, 2523h ; Install new addr for int 23
mov dx, OFFSET IntBrk ; Addr of routine
int Dos ; Store addr of our routine for this interrupt
pop ds ; Restore ds
ret
SETINT ENDP
; NPSEND command -- Non-Protocol SEND of a file
Non_protocol_send PROC
cld ; Forwards
cmp taklev, maxtak ; Hit our limit?
jl NPS_1 ; Continue if still OK
mov ah,prstr
mov dx,offset erms38 ; Complain
int dos
jmp RSkp
NPS_1: mov di,takadr
add di,size takinfo
mov Hold_di, di ; Save reg
mov ah,cmtxt
lea bx,[di].takbuf ; convenient place to parse name into
mov dx,offset filmsg ; Help in case user types "?"
call comnd ; Parse for a text string
jmp RSkp ; Can't get text string (?), give up
mov di, Hold_di ; Restore reg
lea si,[di].takbuf ; get buffer back
mov bl,ah ; length of thing parsed
sub bh, bh ; Zero top half
mov byte ptr [bx+si],0 ; make it asciz
mov ax,si ; point to name again
call spath ; is it around?
jc NPS_2 ; no, go complain
mov di, Hold_di ; Restore reg
mov dx,ax ; point to name from spath
mov ah,open2 ; 2.0 open call
mov al,0 ; open for reading
int dos
jnc NPS_3 ; open ok, keep going
NPS_2: mov ah,prstr
mov dx,offset erms31
int dos
jmp RSkp
NPS_3: inc taklev
mov takadr,di
mov word ptr [di].takfcb+1,ax ; save file descriptor
mov byte ptr [di].takfcb,0feh ; mark as 2.0 file descriptor
mov bx, ax ; need descriptor here
mov ax, (LSeek*256)+2
sub cx, cx
mov dx, cx ; seek 0 bytes from end
int dos
mov [di].takcnt,ax
mov [di].takcnt+2,dx ; store length
mov ax, (LSeek*256)+0
sub cx, cx
mov dx, cx ; now seek back to beginning
int dos
call takrd ; Get a buffer full of data
mov ax, OFFSET cs:Our_entry_point ; Get address of our entry point
call Set_up_script_processor ; Tell emulator to call us (and where)
call Telnet2 ; Enter terminal emulator
nop ; Ignore skip return
nop
nop
jmp RSkp ; All done
; ***** Enter here on exit hooks from Terminal Emulator *****
Our_entry_point:
cld ; Forwards
or ah, ah ; Is the code 0 (keyboard)?
jnz OEP_1 ; No
jmp SHORT NPS_loop ; Yes, go do our stuff
OEP_1: cmp ah, 1 ; Code 1 (host character)?
je OEP_3 ; Yes
cmp ah, 2 ; Code 2 (shutdown)?
jne OEP_3 ; No
jmp NPS_end ; Yes, close files and be ready to be gone
OEP_3: ret ; Unknown, do nothing for now
NPS_loop:
call Get_a_char ; Get the next character
jc NPS_end ; No more, go close the file
mov ah, al ; Copy to ah
call OutChr ; Send this character
jmp NPS_end ; Failed, give up
ret ; Return to terminal emulator for now
NPS_end:
sub ax, ax ; Make a zero
call Set_up_script_processor ; Make emulator forget us
mov bx, TakAdr ; Addr of TAKE frame
mov bx, WORD PTR [bx].TakFcb+1 ; This is where file handle is stored
mov ah, CLOSE2 ; Close file the file handle way
int Dos
dec TakLev ; Untake this file
sub TakAdr, SIZE TakInfo ; Back off space on stack also
ret ; Done here
Get_a_char:
mov bx, TakAdr ; Addr of our TAKE frame
mov ax, [bx].TakCnt ; Get low half of doubleword char count
or ax, [bx].TakCnt+2 ; Merge with high half of doubleword
jz GAC_EOF ; No chars left, flag as end-of-file
cmp [bx].TakChl, 0 ; Disk file, any chars left in buffer?
jne GAC_2 ; Yes, don't need more yet
call TakRd ; Reload buffer from disk file
cmp [bx].TakChl, 0 ; Any chars now?
je GAC_EOF ; No chars even after call to TakRd, EOF!
GAC_2: dec [bx].TakChl ; Decrement for the char
sub [bx].TakCnt, 1 ; DEC doesn't set carry!!
sbb [bx].TakCnt+2, 0
mov si, [bx].TakPtr ; Pick up ptr into buffer
lodsb ; Pick up next char
mov [bx].TakPtr, si ; Store the updated ptr
cmp al, Lf ; Is this a linefeed?
jne GAC_3 ; No
cmp LastChar, Cr ; Was the previous character a return?
jne GAC_3 ; This Lf was not preceded by a Cr
mov LastChar, al ; Update this
jmp Get_a_char ; Char is Lf following Cr, so don't send it
GAC_3: mov LastChar, al ; Update this for next time
cmp al,CtlZ ; Maybe control-z?
je GAC_EOF ; Yes, bomb out
clc ; No error
ret
GAC_EOF:
stc ; Error (EOF)
ret
Non_protocol_send ENDP
; take commands from a file, but allow a path name
Take PROC
cmp taklev,maxtak ; Hit our limit?
jl Take1 ; Continue if still OK
mov ah,prstr
mov dx,offset erms30 ; Complain
int dos
jmp RSkp
Take1: mov di,takadr
add di,size takinfo
mov ah,cmtxt
lea bx,[di].takbuf ; convenient place to parse name into
mov dx,offset filmsg ; Help in case user types "?"
call comnd
jmp RSkp
call Setup_take_file ; Call common routine to do the work
jnc TAK_Got_it ; Ok so far
mov ah,prstr
mov dx,offset erms31
int dos
jmp RSkp
TAK_Got_it:
cmp flags.takflg,0
je Take4
mov ah,prstr
mov dx,offset crlf
int dos
Take4: jmp rskp
Take ENDP
; Setup_take_file -- routine to set up a new TAKE file in a new TAKE frame
;
; call with ...
; di/ New frame address
; ah/ Length of file name
; [di].TakBuf/ File name with optional path ... SPATH is used to fix up
PUBLIC Setup_take_file
Setup_take_file PROC
mov Hold_di, di ; Save frame address
lea si, [di].TakBuf ; Get buffer back
mov bl, ah ; Length of thing parsed
sub bh, bh ; Zero high half
mov BYTE PTR [bx+si], 0 ; Make it ASCIZ
mov ax, si ; Point to name again
call SPath ; Is it around?
jc Take2 ; No, go complain
mov dx, ax ; Point to name from spath
mov ax, (Open2*256) + 0 ; DOS 2.0 open call for read
int Dos
jnc Take3 ; Open ok, keep going
Take2: ret ; Return with carry on ...
Take3: inc TakLev ; Bump our nested TAKE count
mov di, Hold_di ; Restore frame address
mov TakAdr, di ; Advance to next TAKE frame
mov BYTE PTR [di].TakFcb, 0FEh ; Mark as 2.0 file descriptor
mov WORD PTR [di].TakFcb+1, ax ; Save file descriptor
mov bx, ax ; Need descriptor here
mov ax, (LSeek*256) + 2 ; Seek 0 bytes from end
sub cx, cx
sub dx, dx
int Dos
mov [di].TakCnt, ax ; Store length
mov [di].TakCnt+2, dx ; (doubleword)
mov ax, (LSeek*256) + 0 ; Seek back to the beginning
sub cx, cx
sub dx, dx
int Dos
call TakRd ; Get a buffer full of data
clc ; Flag no carry (no error)
ret ; Done here
Setup_take_file ENDP
TAKRD PROC NEAR
push bx
push cx
push dx
mov bx,takadr
cmp byte ptr [bx].takfcb,0feh ; is it a 2.0 file handle?
jne takrd1 ; no, handle differently
push bx ; save frame address
lea dx,[bx].takbuf ; buffer to read into
mov cx,dmasiz ; # of bytes to read
mov ah,readf2 ; 2.0 read call
mov bx,word ptr [bx].takfcb+1 ; file handle is stored here
int dos
pop bx ; restore frame address
jmp takrd2 ; rejoin common exit
takrd1: mov ah,setdma
lea dx,[bx].takbuf
int dos
mov ah,readf
lea dx,[bx].takfcb
int dos
mov ah,setdma
lea dx, DTA
int dos
takrd2: mov [bx].takchl,dmasiz
lea ax,[bx].takbuf
mov [bx].takptr,ax
pop dx
pop cx
pop bx
ret
TAKRD ENDP
; copy the path into pthbuf
; enter with ax/ environment segment address
; works in 2.0 only
getpath proc near
push es
mov bx,ds
mov es,bx ; address data segment
mov bx,offset pthnam ; thing to find
mov cx,pthlen ; length of it
mov dx,offset pthbuf ; place to put it
mov byte ptr pthbuf,0 ; initialize to null..
call getenv ; get environment value
pop es
ret ; and return
getpath endp
; copy the comspec into cmspbuf
; enter with ax/ environment segment address
; works in 2.0 only
getcsp proc near
push es
mov bx,ds
mov es,bx ; address data segment
mov bx,offset cmspnam ; thing to find
mov cx,cmsplen ; length of it
mov dx,offset cmspbuf ; place to put it
call getenv ; get environment value
pop es
ret ; and return
getcsp endp
; find a path variable. Enter with ax/ environment segment,
; bx/ variable to find (incl =), cx/ length of variable name,
; dx/ address to store value at
; The buffer given in dx is unchanged if the variable isn't found
getenv proc near
push ds
push es
mov es,ax ; address segment
mov di,0 ; offset in segment
geten1: cmp es:byte ptr [di],0 ; end?
je geten4 ; yes, forget it
push cx ; save counter
push di ; and offset
mov si,bx
repe cmpsb ; is it the one?
pop di
pop cx ; restore these
je geten2 ; found it, break loop
push cx ; preserve again
mov cx,0ffffh ; bogus length
mov al,0 ; marker to look for
repne scasb ; search for it
pop cx ; restore length
jmp geten1 ; loop thru rest of environment
geten2: add di,cx ; skip to definition
mov si,di ; this is source
mov di,dx ; destination as given
mov ax,ds
mov bx,es
mov ds,bx
mov es,ax ; exchange segment regs for copy
geten3: lodsb ; get a byte
stosb ; drop it off
cmp al,0 ; end of string
jne geten3 ; no, go on
geten4: pop es
pop ds ; restore registers
ret ; and return
getenv endp
%OUT >> About half way through source file
; Put xxxxxxxx.INI onto Take stack if it exists. Just like
; the Take command, except it doesn't read a filename
RdInit PROC
mov bx, TakAdr ; Pick up "current" take address
add bx, SIZE TakInfo ; Bump to next frame
mov si, OFFSET IniNm2 ; Name to try
lea di, [bx].TakBuf ; Ptr to where to put it
mov cx, IniNm2_len ; Length of name
cld ; Forwards
rep movsb ; Copy the string
mov di, bx ; Copy pointer to TAKE frame to di
mov ah, IniNm2_len ; Length in ah
call Setup_take_file ; Call common routine to do the work
jnc RDI_Got_it ; Ok so far
ret ; If no file, give up !
RDI_Got_it:
jmp rskp
RdInit ENDP
; get command line into a macro buffer
gcmdlin proc near
push ds
push es
cld
mov es,psp ; address psp
mov ch,0
mov cl,es:[cline] ; length of cmd line
mov di,cline+1 ; point to actual line
mov al,' '
jcxz gcmdl3 ; no command line, forget it
repe scasb ; skip over spaces
je gcmdl3 ; all spaces, forget it
mov si,di ; this is first non-space
dec si ; pre-incremented..
inc cx
inc taklev ; bump take level
add takadr,size takinfo ; address new take frame
mov bx,takadr
mov byte ptr [bx].takfcb,0ffh ; mark as a macro
push cx ; save length
push ds ; and segment
lea di,[bx].takbuf ; into take buffer
mov ax,ds
mov ds,psp
mov es,ax ; switch segments for copy
gcmdl1: lodsb ; get a byte
cmp al,',' ; comma?
jne gcmdl2 ; no, keep going
mov al,cr ; convert to cr
gcmdl2: stosb ; deposit it
loop gcmdl1 ; copy whole cmd
pop ds ; restore segment
pop cx ; restore len
lea ax,[bx].takbuf
mov [bx].takptr,ax ; init buffer ptr
mov [bx].takchl,cl ; chars remaining
mov [bx].takcnt,cx ; and all chars
mov [bx].takcnt+2,0 ; clear high order
gcmdl3: pop es
pop ds
ret
gcmdlin endp
; This routine prints the prompt and specifies the reparse address
PROMPT PROC NEAR
mov comand.cmprmp,dx ; save the prompt
pop bx ; Get the return address
mov comand.cmrprs,bx ; Save as addr to go to on reparse
mov comand.cmostp,sp ; Save for later restoral
push bx ; Put it on the stack again
mov bx,offset comand.cmdbuf
mov comand.cmcptr,bx ; Initialize the command pointer
mov comand.cmdptr,bx
mov ah,0
mov comand.cmaflg,ah ; Zero the flags
mov comand.cmccnt,ah
mov comand.cmsflg,0FFH
cmp flags.takflg,0 ; look at take flag
jne promp1 ; supposed to echo, skip this check..
cmp taklev,0 ; inside a take file?
je promp1 ; no, keep going
ret ; yes, return
promp1: mov ah,prstr ; Print the prompt
mov dx,comand.cmprmp
int dos
ret
PROMPT ENDP
; Erase specified file(s)
DELETE PROC NEAR
mov comand.cmcr,0 ; Filename must be specified
mov ah,cmifi ; Parse an input filespec
mov dx,offset fcb
mov bx,offset filhlp2 ; Text of help message
call comnd
jmp r ; Bad filename
mov ah,cmcfm ; Parse a confirm
call comnd
jmp r
mov flags.nmoflg, 0 ; Function CMIFI leaves this set, clear it
cld
mov di,offset fcb+1
mov al,'?'
mov cx,11 ; # of chars in a name
repe scasb ; are they all ?'s?
jne del1 ; no, skip message
mov dx,offset infms1
call prompt
mov ah,cmkey
mov dx,offset yestab
mov bx,0
call comnd
jmp r
push bx
mov ah,cmcfm
call comnd
pop bx
ret
nop
pop bx
cmp bx,0
jne del1
jmp rskp
del1: mov dx,offset fcb
mov ah,sfirst ; See if any files match this specification
int dos
cmp al,0FFH ; No matches?
jne del2
mov ah,prstr
mov dx,offset erms32
int dos
jmp rskp
del2: mov dx,offset fcb
mov ah,delf ; Erase the file(s)
int dos
mov dx,offset infms8
mov ah,prstr ; Say we did so
int dos
jmp rskp
DELETE ENDP
CHKDSK PROC
mov ah,cmcfm
call comnd
jmp r
mov ah, PrStr ; Code to type string
mov dx, OFFSET CrLf ; A CrLf
int Dos ; Type it
mov si,offset chkdcmd ; point to cmd
mov cx,chkdlen ; and length
jmp crun ; and go execute it nicely
CHKDSK ENDP
DIRECT PROC
mov ah,dosver ; See what level of DOS we're at
int dos
cmp al,0 ; Level 2.0 or above?
jne dir4 ; Yes - get directory the easy way
mov comand.cmcr,1 ; Allow plain CR (so DIR == DIR *.*)
mov ah,cmifi ; Get input file spec
mov dx,offset fcb ; Give the address for the FCB
mov bx,offset filhlp3
call comnd
jmp r
mov ah,cmcfm ; Parse a confirm
call comnd
jmp r
mov comand.cmcr,0 ; Reset this
push es
mov ax,ds
mov es,ax
mov temp1,0FFH
mov di,offset nambuf
dir0: call getfn ; Get a matching file name
cmp al,0FFH ; Retcode -- are we done?
je dir1 ; Yes, just leave
call dumpit ; Print it or dump to buffer
jmp dir0
dir1: pop es
jmp rskp
dir4: mov si,offset cmspbuf ; command processor
mov di,offset dirnam
dir5: lodsb ; get a byte
or al,al
jz dir6 ; stop on the null
stosb ; otherwise copy it in
jmp dir5 ; and keep going
dir6: mov si,offset dircmd ; add directory command to it
mov cx,dirclen
rep movsb
mov ah,cmtxt ; parse with cmtxt so we can have paths..
mov bx,di ; next available byte
mov dx,offset filwmsg ; In case user wants help
mov Hold_di, di
call comnd
jmp r
mov di, Hold_di
mov cl,ah
mov ch,0 ; length of name
sub di,offset dirnam ; compute # of bytes used
add cx,di
mov si,offset dirnam ; dir cmd
jmp crun ; join run cmd from there
DIRECT ENDP
getfn: cmp temp1,0FFH
jne gtfn1
mov ah,sfirst ; Any matches?
mov dx,offset fcb
int dos
cmp al,0FFH ; Means no matches
je gtfn5
call savfcb
mov temp1,0
jmp gtfn4
gtfn1: cmp flags.wldflg,0FFH ; Wilcard seen?
je gtfn2 ; Yes, get next file
mov al,0FFH ; No, set retcode
ret
gtfn2: call rstfcb
mov ah,snext
mov dx,offset fcb
int dos
cmp al,0 ; Any more matches?
je gtfn3 ; Yes keep going
mov al,0FFH ; OK return code
ret
gtfn3: call savfcb
gtfn4: push di
mov si,offset DTA ; Data is here
mov di,offset fcb ; Copy to here
mov cx,37
repne movsb
pop di
call nicnam ; Make name nice for printing
mov al,0
ret
gtfn5: mov ah,prstr ; Don't print if a server
mov dx,offset erms32 ; Say no matches
int dos
mov al,0FFH ; Failure return code
ret
savfcb: push di
mov si,offset fcb ; Data is here
mov di,offset cpfcb ; Copy to here
mov cx,37
repne movsb
pop di
ret
rstfcb: push di
mov si,offset cpfcb ; Data is here
mov di,offset fcb ; Copy to here
mov cx,37
repne movsb
pop di
ret
nicnam: mov al,CR ; Add CRLF before print names
stosb
mov al,LF
stosb
mov cx,8
mov si,offset fcb+1
repne movsb ; Get the file name
mov al,' '
stosb
mov cx,3
repne movsb
mov al,tab
stosb
mov al,' '
stosb
mov ah,openf
mov dx,offset fcb
int dos
mov bx,offset fcb+18 ; Get hi order word of file size
mov ax,[bx]
mov dx,ax
mov bx,offset fcb+16 ; Get lo order word
mov ax,[bx]
call nout2x ; Get it in decimal
mov al,tab
stosb
mov al,' '
stosb
mov ah,0
mov si,offset fcb+20
lodsb
mov fildat+1,al
lodsb
mov fildat,al ; Date field of fcb
mov cl,5
shr fildat+1,cl
and fildat,1
mov cl,3
shl fildat,cl
mov al,fildat
or al,fildat+1 ; Get the month field
cmp al,9
jg nic0
push ax
mov al,' '
stosb
pop ax
nic0: call nout2 ; Make it decimal
mov al,'-'
stosb
mov si,offset fcb+20 ; Get date field
lodsb
and al,1FH
cmp al,10 ; Only one digit?
jge nic0x
push ax
mov al,'0' ; Make it two digits
stosb
pop ax
nic0x: call nout2 ; Make it decimal
mov al,'-'
stosb
mov si,offset fcb+21 ; Get the year field
lodsb
shr al,1
add al,80
cmp al,100 ; At the year 2000 or above?
js nic0y ; No, just go on
sub al,100 ; Go back to two digits
nic0y: cmp al,10 ; Only one digit?
jge nic0z
push ax
mov al,'0' ; Make it two digits
stosb
pop ax
nic0z: call nout2 ; Make it decimal
mov al,tab
stosb
mov si,offset fcb+23 ; Get time field of fcb
lodsb
mov cl,3 ; Get the hour field
shr al,cl
mov tmp,'a' ; For AM
cmp al,12 ; Before noon?
jl nic1
mov tmp,'p' ; It's PM
je nic1 ; Don't change "12" to "0"
sub al,12 ; Use a 12 hr. clock
nic1: cmp al,0 ; Just after midnight?
jne nic1x
add al,12 ; Make it "12" instead of "0"
nic1x: cmp al,10 ; Pad with a space?
jge nic2
push ax
mov al,' '
stosb
pop ax
nic2: call nout2 ; Make it decimal
mov al,':' ; Separate hours and minutes
stosb
mov si,offset fcb+23 ; Get the minutes field
lodsb
and al,07
mov cl,3
shl al,cl
mov ah,al
mov si,offset fcb+22
lodsb
mov cl,5
shr al,cl
or al,ah
mov ah,0
cmp al,10 ; Would there be a leading zero
jge nic3
push ax
mov al,'0'
stosb
pop ax
nic3: call nout2 ; Make it decimal
mov al,tmp ; Add 'a' (AM) or 'p' (PM)
stosb
mov ah,closf
mov dx,offset fcb
int dos
ret
; For now, just print it
dumpit: mov al,'$'
stosb
mov ah,prstr
mov dx,offset nambuf
int dos
mov di,offset nambuf
ret
; Push to an inferior command parser
DoPush PROC
mov ah,cmcfm ; Confirm the command
call comnd
jmp r
; Entry to pre-confirmed Push command ...
; so we can Push from FT screen, for example ...
Quick_Push:
mov si,offset cmspbuf ; name of parser
push si ; save beginning
sub cx,cx ; initial length
dopus2: lodsb
inc cx ; count this
or al,al ; at end?
jnz dopus2 ; no, keep going
pop si ; restore cmd
dec cx ; this is incremented one over
jmp short crun ; go run it
dopush endp
; crun - run an arbitrary program. Enter with si/address of whole
; cmd, cx/length of cmd
CRUN proc near
push cx ; save length of cmd
mov ax,ds
mov es,ax ; address dest segment
mov di,offset nambuf
rep movsb ; copy command so we can mess with it
pop cx
mov si,offset nambuf ; point to command
jmp short run3 ; and join run code
CRUN ENDP
RUN PROC NEAR
mov ah,cmtxt ; Get program name
mov bx,offset nambuf ; Convenient buffer
mov dx,offset filmsg ; In case user wants help
call comnd
nop
nop
nop
cmp ah,0
jne run2
mov ah,prstr
mov dx,offset erms35
int dos
jmp rskp
run2: mov cl,ah
mov ch,0
mov si,offset nambuf
; alternate entry if cmd is already known. Source cmd ptr in si
; is trashed
run3: mov bx,cx
mov byte ptr [si+bx],cr ; end string with a cr for dos
mov di,offset cmdnam
mov ax,ds
mov es,ax
run4: lodsb
cmp al,' '
jne run5
dec si ; back up over space
jmp short run6 ; and exit loop
run5: stosb
loop run4
run6: mov byte ptr [di],0 ; terminate string
dec si ; point back a byte into argument
mov [si],cl ; put length of argument here
mov exearg+2,si ; pointer to argument string
mov exearg+4,ds ; segment of same
inc si ; pass length over
mov al,1 ; scan leading separators
mov di,offset exefcb ; parse into this fcb
mov ah,prsfcb
int dos ; go parse the fcb
mov al,1 ; scan leading separators
mov di,offset exefcb2 ; second fcb to fill
mov ah,prsfcb
int dos ; parse the fcb
mov ax,ds
mov es,ax ; put es segment back
mov ax,offset cmdnam ; point to cmd name again
call spath ; look for it
jc run8 ; not found, go complain
mov dx,ax ; point to command name
call Restore_normal_error_handling ; Return to normal error handler
; At this point, we are about to run the program. But first, save the
; current settings of the three "user vectors", "Terminate", "Ctrl-Break Exit"
; and "Critical Error Handler". Seems DOS 2.0 and 2.1 don't restore them
; properly after this call. The program worked under DOS 3.0 and 3.1
; without this code
push es ; First, save the incoming es
mov ax, 3522h ; Code to Get Vector, starting at 22h
int Dos ; Get it
push es ; Save his segment
push bx ; and offset
inc al ; 23h -- second vector to get
int Dos ; Get it
push es ; Save his segment
push bx ; and offset
inc al ; 24h -- third vector to get
int Dos ; Get it
push es ; Save his segment
push bx ; and offset
; Run the program
mov ax, ds ; Copy ds
mov es, ax ; to es
mov ax, (256*Exec)+0 ; Code to load and execute a program
mov bx, OFFSET exearg ; Point to arguments
mov ssave, sp ; Save stack ptr
int Dos ; Go run the program
; Just came back from (maybe) running the program, have to restore some
; fundamental items first
mov ax,seg datas
mov ds, ax ; reset data segment
mov ax,seg stack
mov ss,ax ; and stack segment
mov sp,ssave ; restore stack ptr
; Now restore the things DOS 2.0 and 2.1 let be clobbered
mov ah, 25h ; Code to Set Interrupt Vector
mov al, 24h ; Vector to restore
pop dx ; Get back offset
pop ds ; Get back segment
int Dos ; Set it up
mov al, 23h ; Vector to restore
pop dx ; Get back offset
pop ds ; Get back segment
int Dos ; Set it up
mov al, 22h ; Vector to restore
pop dx ; Get back offset
pop ds ; Get back segment
int Dos ; Set it up
pop es ; Get back saved es reg
mov ax, SEG DataS ; DS just got clobbered
mov ds, ax ; Fix it up again
call Set_up_error_handling ; Establish our routine as the one to use
pushf ; Save flags
mov ah, SetDMA
mov dx, OFFSET DTA
int Dos ; Restore DTA address!!
popf ; Recover flags
jc Run8 ; Error
mov ah, PrStr
mov dx, OFFSET CrLf
int Dos ; Type another blank line
jmp rskp ; Return
run8: mov ah,prstr
mov dx,offset erms37
int dos
jmp rskp
RUN ENDP
; the show command
showcmd proc near
mov ah,cmkey
mov dx,offset shotab
xor bx,bx ; no canned help
call comnd
jmp r
call bx ; call the handler
jmp r
jmp rskp ; and return
showcmd endp
PUBLIC IntBrk
IntBrk PROC FAR
push ax ; Interrupt routine, not allowed to
push ds ; clobber registers
pushf ; Save flags also
mov ax, SEG Datas
mov ds, ax
cmp flags.debug, 1 ; Debug mode?
je IntB2 ; Yes, then don't ignore the ^C
mov flags.cxzflg, 'C' ; Say we saw a ^C
mov pack.state, 'A' ; Set the state to abort
popf ; Restore flags
pop ds ; Restore regs
pop ax
cmp ah, 09h ; Were we typing a string?
jne IntBl1 ; No
add sp, 6 ; We want to return to AFTER the "int Dos" that
; started us typing ... otherwise this call
; retypes the string completely!
IntBl1: iret ; That's it for this ^C
IntB2: popf ; Restore flags
pop ds ; Restore regs
pop ax
jmp in3ad ; Original break interrupt address
IntBrk ENDP
; Set the maximum data packet size. [21b]
PACKLEN PROC NEAR
mov ah,trans.spsiz ; Maximum send packet size
sub ah,4 ; Size minus control info
sub ah,trans.chklen ; And minus checksum chars
sub ah,2 ; Leave room at end: 2 for possible #X
cmp trans.ebquot,'N' ; Doing 8-bit quoting?
je pack0 ; Nope so we've got our size
cmp trans.ebquot,'Y'
je pack0 ; Not doing it in this case either
sub ah,1 ; Another 1 for 8th-bit quoting
pack0: mov trans.maxdat,ah ; Save max length for data field
ret
PACKLEN ENDP
NOUT2 PROC NEAR
push ax
push dx
mov temp,10 ; Divide quotient by 10
cwd ; Convert word to doubleword
div temp ; AX <-- Quo, DX <-- Rem
cmp ax,0 ; Are we done?
jz nout0 ; Yes
call nout2 ; If not, then recurse
nout0: add dl,'0' ; Make it printable
mov temp,ax
mov al,dl
stosb
mov ax,temp
pop dx
pop ax
ret ; We're done. [21c]
NOUT2 ENDP
NOUT2X PROC NEAR
push ax
push dx
push cx
mov temp,10 ; Divide quotient by 10
div temp ; AX <-- Quo, DX <-- Rem
mov cx,dx ; Remember the remainder
cmp ax,0 ; Are we done?
jz nout0x ; Yes
mov dx,0
call nout2 ; If not, then recurse
nout0x: add cl,'0' ; Make it printable
mov temp,ax
mov al,cl
stosb
mov ax,temp
pop cx
pop dx
pop ax
ret ; We're done. [21c]
NOUT2X ENDP
SPATH PROC
; enter with ax/ ptr to file name. Searches path for given file,
; returns with ax/ ptr to whole name, or carry on if file isn't
; to be found
; First, see if file is in connected directory
push ax
call isfile ; See if file exists in current dir
pop ax ; AX is clobbered
jc spath_0 ; Not here, look down PATH ...
jmp SPath_Exit ; That was easy ... found file
spath_0:
push es
mov bx,ds
mov es,bx ; address data segment
mov bx,ax ; convenient place to keep this
mov si,ax
mov di,offset tfile ; place to copy to
mov dl,0 ; no '\' seen yet
mov ah,swchar ; get switch character
spath1: lodsb
stosb
cmp al,ah ; contain path characters?
jne spath2 ; no, keep going
mov dl,1 ; remember we've seen them
spath2: or al,al
jnz spath1 ; copy name in
or dl,dl ; look at flag
jz spath3 ; no path embedded, keep going
pop es
mov ax,offset tfile ; else..
call isfile
mov ax,offset tfile ; point to right thing..
jmp SPath_Exit ; let isfile decide and return
; no path, keep going
spath3: mov si,offset pthbuf ; path definition
cmp byte ptr [si],0 ; empty path?
jne spath4 ; no, keep going
mov ah,gcd ; get current dir
mov dl,0 ; for default drive
mov si,offset defpth+1 ; place to put it
int dos
mov si,offset defpth ; point to the path
spath4: cmp byte ptr [si],0 ; null, exit loop
je spath9
mov di,offset tfile ; place to put name
spath5: lodsb ; get a byte
cmp al,';' ; end of this part?
je spath7 ; yes, break loop
cmp al,0 ; maybe end of string?
jne spath6 ; no, keep going
dec si ; back up over it
jmp short spath7 ; and break loop
spath6: stosb ; else stick in dest string
jmp spath5 ; and continue
spath7: push si ; save this ptr
mov si,bx ; this is user's file name
mov al,swchar ; get switch character
cmp byte ptr [di-1],al ; does it end with switch char?
je spath8 ; yes, don't put one in
stosb ; else add one
spath8: lodsb
stosb
or al,al
jnz spath8 ; copy rest of name
pop si ; restore pos in path def
mov ax,offset tfile
call isfile ; is it a file?
jc spath4 ; no, keep looking
mov ax,offset tfile
pop es
jmp SPath_Exit ; return success (carry off)
spath9: pop es ; restore this
mov ax,bx ; not found yet, get original path
call isfile ; does it exist?
SPath_Exit:
ret
spath endp
Set_up_error_handling PROC
cmp Error_handling_installed, 0 ; Have this in already?
jne SUE_Z ; Yes, quit
mov Error_handling_installed, 1 ; Flag as already done
pushf ; Save these regs
push ax
push bx
push dx
push es ; Save starting value of es
mov ax, 3524h ; Code to get current interrupt vector 24h
int Dos ; Get address of this interrupt
mov WORD PTR cs:Critical, bx ; Store offset (IP)
mov ax, es ; Copy es to ax
mov WORD PTR cs:Critical+2, ax ; Store the segment (CS)
pop es ; Get back normal es
push ds ; Save ds
mov ax, cs ; Copy cs ...
mov ds, ax ; ... to ds
mov ax, 2524h ; Install new addr for int 24
mov dx, OFFSET Handle_error ; Addr of routine
int Dos ; Store addr of our routine for this interrupt
pop ds ; Restore ds
pop dx ; Restore regs
pop bx
pop ax
popf
SUE_Z: ret
Set_up_error_handling ENDP
Restore_normal_error_handling PROC
cmp Error_handling_installed, 0 ; Anything to undo?
je RNE_Z ; No, quit
mov Error_handling_installed, 0 ; Flag as back to normal
pushf ; Save these regs
push ax
push dx
push ds
mov dx, WORD PTR cs:Critical ; Set up the offset
mov ax, WORD PTR cs:Critical+2 ; Set up the segment
mov ds, ax ; in ds ...
mov ax, 2524h ; Code to set interrupt vector 24h
int Dos ; Store addr of original routine
pop ds ; Restore regs
pop dx
pop ax
popf
RNE_Z: ret
Restore_normal_error_handling ENDP
PUBLIC Handle_error, Get_Error
; PUBLIC Set_up_error_handling
; PUBLIC Restore_normal_error_handling
Handle_error PROC FAR
push bx ; Save bx
mov bx, di ; Copy di to bx
sub bh, bh ; Clear the high half
mov CS:Last_Critical_Error, bx ; Save for non-interrupt level rtns
or ah, ah ; Test for disk errors
js Not_disk ; It isn't disk
cmp bl, 0 ; Compare with code for "Write protect"
je Fail_It ; Yes, make the original DOS call fail
cmp bl, 2 ; Compare with code for "Drive not ready"
je Fail_It ; Yes, make the original DOS call fail
jmp SHORT As_usual ; Treat it like any error
Not_disk:
cmp bl, 09h ; Compare with code for "Printer out of paper"
je Fail_It ; Yes, make the original DOS call fail
cmp bl, 0Ah ; Compare with code for "Write fault"
je Fail_It ; Yes, make the original DOS call fail
; jmp SHORT As_Usual ; Fall thru to ...
As_Usual:
pop bx ; Restore bx
jmp cs:Critical ; Go run original critical error handler
Fail_It:
pop bx ; Restore bx
; We must now set things up as if the original function call had failed ...
add sp, 6 ; Flush DOS's 24h return regs
; IBM PC-DOS 2.0 manual (page D-7) says that you can "return to the program
; immediately after the INT 21 that experienced the error. Note that if
; this is done, DOS will be in an unstable state until a function call
; higher than 12 is issued."
; Based on this warning, lets do a harmless high numbered DOS call
; to "stabilize" DOS
mov ah, 30h ; Code to get vendor number
int Dos ; Ask Dos to stabilize itself
; Restore registers to what they were before the "failing" call
pop ax
pop bx
pop cx
pop dx
pop si
pop di
mov bp, sp ; Copy stack ptr to bp
or WORD PTR [bp+10], 1 ; Turn on carry bit also, to cover all cases
pop bp ; Then restore real bp
pop ds
pop es
mov al, 0FFh ; Set up al to look like a failure
iret ; Return from interrupt to the original
; "failing" Dos call
Handle_error ENDP
Get_Error PROC
mov ax, Last_Critical_Error ; Pick this up
mov Last_Critical_Error, 0FFh ; Make this impossible
ret ; Run with it
Get_Error ENDP
; Returns with carry off if the file pointed to by ax exists
isfile PROC
mov dx,ax ; copy ptr
mov ax, (chmod*100h)+0 ; chmod function, read file attribute
int dos
ret ; dos sets carry
isfile endp
; Get_memory_block -- Get a single block of memory
;
; Call with ...
; bx/ number of paragraphs of memory requested
;
; Returns with ...
; carry flag set if failure
; ax/ high-order 16 bits of address of block if success
Get_memory_block PROC
mov ax, Count_of_allocated_blocks ; Get current index
cmp ax, Max_count_of_blocks ; Too high?
jae GMB_bomb ; Too many already
mov ah, 48h ; Code to get a memory block
int Dos ; Ask DOS for the memory
jc GMB_bomb ; No such luck
mov bx, Count_of_allocated_blocks ; Get current index
shl bx, 1 ; Double for word offset
mov Block_table[bx], ax ; Store ptr to the block
inc Count_of_allocated_blocks ; Update index
ret ; Done here
GMB_bomb:
stc ; Set error flag if not already set
ret ; Return failure
Get_memory_block ENDP
; Return_memory_block -- Return a single block of memory
;
; Call with ...
; bx/ high-order 16 bits of address of block to be returned
Return_memory_block PROC
ret ; Not sure we need this routine
Return_memory_block ENDP
; Return_all_memory -- Return all allocated blocks of memory
Return_all_memory PROC
push es ; We modify es
mov cx, Count_of_allocated_blocks ; The number of blocks we have
jcxz RAM_done ; Done if no blocks
sub bx, bx ; Start at index zero
RAM_loop:
mov ax, Block_table[bx] ; Get the next entry
mov es, ax ; Copy to es
mov ah, 49h ; Code to Free Allocated Memory
int Dos ; Ask DOS to release this memory
inc bx ; Move to word in table
inc bx
loop RAM_loop ; Go do the next one if any
RAM_done:
pop es ; Restore es
mov Count_of_allocated_blocks, cx ; Zero the count of blocks
ret ; Go home
Return_all_memory ENDP
; Routines to disable and reenable interrupts
NoInt PROC
cli ; We want interrupts off
inc NoInt_Count ; Bump our nested count
ret ; That's it
NoInt ENDP
OkInt PROC
dec NoInt_Count ; Unbump our nested count
jnz OkInt_unchanged ; Still nested in a NOINT, just return
sti ; Back to unnested state, turn ints back on
OkInt_unchanged:
ret ; Done here
OkInt ENDP
; Jumping to this location is like retskp. It assumes the instruction
; after the call is a jmp addr
RSKP PROC NEAR
pop bp
add bp,3
push bp
; ret
RSKP ENDP
; Jumping here is the same as a ret
R PROC NEAR
ret
R ENDP
Code ENDS ; End of code section
Stack SEGMENT PARA STACK 'STACK'
DW 512 DUP(?) ; Our stack
Stk EQU THIS WORD
Stack ENDS
END Start